# -*- coding: utf8 -*-
"""
    demon
"""

import os
import signal
import psutil
import time
import sys
from multiprocessing import Process, active_children

import logging
logger = logging.getLogger('bt_server')


class Daemon(object):
    def __init__(self, name, pid_file=None):
        self._name = name
        self._pid_file = pid_file
        self._pid = None
        self._process = None

        if not pid_file:
            return

        # 获取进程id(pid)
        try:
            with open(self._pid_file, 'r', 0) as f:
                self._pid = int(f.readline())
                logger.debug('-> 读取文件: {} pid: {}'.format(self._pid_file, self._pid))
        except IOError:
            return None

        # 查询进程id(pid)
        try:
            self._process = psutil.Process(self._pid)
            logger.debug('-> "{}" 状态: {}'.format(self._name, self._process.status()))
        except psutil.NoSuchProcess:
            logger.debug('-> "{}" 状态: not exist'.format(self._name))
            self._pid = None
            os.remove(self._pid_file)

    def __hash__(self):
        return hash(self._name)

    def _signal_handle(self, signum, frame):
        logger.debug('"{}" get signal: {}'.format(self.name, signum))
        logger.warning('已停止"{}".'.format(self.name))
        if self._pid_file:
            try:
                os.remove(self._pid_file)
                logger.debug('-> 移除 pid file: {}...'.format(self._pid_file))
            except OSError:
                pass
        sys.exit(-1)

    def _register_signal_handle(self):
        signums = (signal.SIGHUP, signal.SIGINT,
                   signal.SIGTERM)
        for signum in signums:
            signal.signal(signum, self._signal_handle)

    @property
    def name(self):
        return self._name

    @property
    def pid(self):
        return self._pid

    @property
    def status(self):
        if not self._process:
            return None
        return self._process.status()

    def is_running(self):
        if not self._process:
            return False
        return self._process.is_running()

    def run_infinite(self):
        raise Exception('请在继承类中实现"run_infinite"方法!')

    def _run_loop(self):
        self._register_signal_handle()
        self.run_infinite()

    def start(self, *args, **kwargs):
        if self.is_running():
            logger.info('-> "{}" 已经启动 pid: {}.'.format(self.name, self.pid))
            return
        p = Process(target=self._run_loop, name=None, args=args, kwargs=kwargs)
        p.daemon = True
        p.start()
        self._pid = p.pid
        # logger.info('===> {}'.format(self._pid))
        self._process = psutil.Process(self._pid)

        if self._pid_file:
            with open(self._pid_file, 'w') as f:
                f.write(str(self._pid))
                logger.debug('-> 创建 pid file: {}...'.format(self._pid_file))
        logger.info('-> "{}" 启动成功 pid: {}.'.format(self.name, self.pid))

    def stop(self):
        if self.is_running():
            self._process.kill()
            self._process = None
            self._pid = None
            logger.info('-> "{}" 已停止'.format(self.name))

        if self._pid_file:
            try:
                os.remove(self._pid_file)
                logger.debug('-> 移除 pid file: {}...'.format(self._pid_file))
            except OSError:
                pass

    def restart(self):
        self.stop()
        self.start()


class DaemonMonitor(object):

    def __init__(self, daemon_check_interval=10):
        self._daemons = set()
        self._daemon_check_interval = daemon_check_interval
        self._loop_process = None

    def _signal_handle(self, signum, frame):
        logger.debug('"守护进程监控" get signal: {}'.format(signum))
        logger.warning('已停止守护进程监控.')
        sys.exit(-1)

    def _register_signal_handle(self):
        signums = (signal.SIGHUP, signal.SIGINT,
                   signal.SIGTERM)
        for signum in signums:
            # logger.info(signum)
            signal.signal(signum, self._signal_handle)

    @property
    def daemon_check_interval(self):
        return self._daemon_check_interval

    @daemon_check_interval.setter
    def daemon_check_interval(self, value):
        self._daemon_check_interval = value

    def add_daemon(self, daemon):
        assert self._loop_process is None, '已启动,请在start()前调用!'
        logger.info('添加 "{}" daemon.'.format(daemon.name))
        self._daemons.add(daemon)

    def _run_monitor_loop(self):
        self._register_signal_handle()

        logger.info('重启守候进程...')
        for daemon in self._daemons:
            try:
                daemon.restart()
            except Exception as e:
                logger.error('-> {} 启动失败!'.format(daemon.name))
                raise

        while True:
            time.sleep(self._daemon_check_interval)
            active_children()
            # logger.debug('检测守候进程状态...')
            for daemon in self._daemons:
                if daemon.is_running():
                    # logger.debug('-> {} [运行中] pid: {} status: {}'.format(daemon.name, daemon.pid, daemon.status))
                    pass
                else:
                    logger.debug('-> {} [已停止], 正在重启...'.format(daemon.name))
                    try:
                        daemon.restart()
                    except Exception as e:
                        logger.error('{} 启动失败!'.format(daemon.name))
                        logger.error(e)

    def start(self):
        assert self._loop_process == None, '已启动,不能重复调用!'
        assert len(self._daemons) > 0, '请先添加守护实例!'

        self._loop_process = Process(target=self._run_monitor_loop)
        # self._loop_process.daemon = True  # damon 进程不能创建新进程
        self._loop_process.start()
